Getting started
Quick start
julia> using Tensorialjulia> x = Vec{3}(rand(3)); # constructor analogous to SArray.jljulia> A = @Mat rand(3,3); # @Vec, @Mat, and @Tensor, analogous to @SVector, @SMatrix, and @SArrayjulia> A ⊡ x ≈ A * x # single contraction (⊡)truejulia> A ⊡₂ A ≈ A ⋅ A # double contraction (⊡₂)truejulia> x ⊗ x ≈ x * x' # tensor product (⊗)truejulia> (@einsum x[i] * A[j,i] * x[j]) ≈ x ⋅ (A' * x) # Einstein summation with @einsumtruejulia> S = rand(Tensor{Tuple{@Symmetry{3,3}}}); # symmetric tensor S₍ᵢⱼ₎julia> SS = rand(Tensor{Tuple{@Symmetry{3,3}, @Symmetry{3,3}}}); # symmetric tensor SS₍ᵢⱼ₎₍ₖₗ₎julia> inv(SS) ⊡₂ S ≈ @einsum inv(SS)[i,j,k,l] * S[k,l] # works as expectedtruejulia> δ = one(Mat{3,3}) # identity tensor3×3 Tensor{Tuple{3, 3}, Float64, 2, 9}: 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0julia> gradient(identity, S) ≈ one(SS) # ∂Sᵢⱼ/∂Sₖₗ = (δᵢₖδⱼₗ + δᵢₗδⱼₖ) / 2true
Defining tensors
1. Tensor
All tensors in Tensorial.jl are represented by the type Tensor{S, T, N, L}, where the type parameters have the following meanings:
S: the tensor size, specified as aTuple(for example, a 3×2 tensor is written asTensor{Tuple{3,2}})T: the element type, whereT <: RealN: the tensor orderL: the number of independent components
The type parameters N and L do not need to be specified when Constructing tensors, since they can be inferred from the size parameter S. When using Tensor fields in a struct, however, it is necessary to declare all type parameters explicitly to ensure type stability, as shown below:
struct MyBadType{T} # all bad
A::Tensor{Tuple{3,3}, Float64}
B::Tensor{Tuple{3,3}, T}
C::Tensor{Tuple{@Symmetry{3,3}}, T, 2}
end
struct MyGoodType{T, dim, L, TT <: Tensor} # all good
A::Tensor{Tuple{3,3}, Float64, 2, 9}
B::Tensor{Tuple{3,3}, T, 2, 9}
C::Tensor{Tuple{@Symmetry{dim,dim}}, T, 2, L}
D::TT
endThe type parameters N and L can be checked using the @Tensor macro:
julia> @Tensor{Tuple{@Symmetry{3,3,3}}}Tensor{Tuple{Symmetry{Tuple{3, 3, 3}}}, T, 3, 10} where T
2. Symmetry
Specifying tensor symmetry can improve performance, since Tensorial.jl eliminates duplicate computations. Symmetry can be encoded in the size parameter S using Symmetry{...}. The @Symmetry macro simplifies this by letting you omit Tuple, as in @Symmetry{3,3}.
Below are some examples:
- $A_{(ij)}$ with 3×3:
Tensor{Tuple{@Symmetry{3,3}}} - $A_{(ij)k}$ with 3×3×3:
Tensor{Tuple{@Symmetry{3,3}, 3}} - $A_{(ijk)}$ with 3×3×3:
Tensor{Tuple{@Symmetry{3,3,3}}} - $A_{(ij)(kl)}$ with 3×3×3×3:
Tensor{Tuple{@Symmetry{3,3}, @Symmetry{3,3}}}
where parentheses in the indices denote symmetry.
Aliases
const Vec{dim, T} = Tensor{Tuple{dim}, T, 1, dim}
const Mat{m, n, T, L} = Tensor{Tuple{m, n}, T, 2, L}
const SecondOrderTensor{dim, T, L} = Tensor{NTuple{2, dim}, T, 2, L}
const FourthOrderTensor{dim, T, L} = Tensor{NTuple{4, dim}, T, 4, L}
const SymmetricSecondOrderTensor{dim, T, L} = Tensor{Tuple{@Symmetry{dim, dim}}, T, 2, L}
const SymmetricFourthOrderTensor{dim, T, L} = Tensor{NTuple{2, @Symmetry{dim, dim}}, T, 4, L}